package jnpf.base.util;


import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.xuyanwu.spring.file.storage.FileInfo;
import com.alibaba.fastjson.JSONArray;
import jnpf.base.UserInfo;
import jnpf.base.entity.VisualdevEntity;
import jnpf.base.model.ExportSelectedModel;
import jnpf.base.model.VisualWebTypeEnum;
import jnpf.base.vo.DownloadVO;
import jnpf.config.ConfigValueUtil;
import jnpf.database.model.dto.PrepareSqlDTO;
import jnpf.database.model.entity.DbLinkEntity;
import jnpf.database.util.ConnUtil;
import jnpf.database.util.DataSourceUtil;
import jnpf.database.util.JdbcUtil;
import jnpf.engine.entity.FlowEngineEntity;
import jnpf.engine.entity.FlowTaskEntity;
import jnpf.engine.model.flowengine.FlowModel;
import jnpf.engine.service.FlowEngineService;
import jnpf.engine.service.FlowTaskNewService;
import jnpf.engine.service.FlowTaskService;
import jnpf.engine.util.FlowDataUtil;
import jnpf.exception.DataException;
import jnpf.exception.WorkFlowException;
import jnpf.model.visiual.*;
import jnpf.model.visiual.fields.FieLdsModel;
import jnpf.util.*;
import jnpf.util.context.SpringContext;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.web.multipart.MultipartFile;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 可视化工具类
 *
 * @author JNPF开发平台组
 * @version V3.1.0
 * @copyright 引迈信息技术有限公司
 * @date 2021年3月13日16:37:40
 */
@Slf4j
public class VisualUtils {


    private static DataSourceUtil dataSourceUtil = SpringContext.getBean(DataSourceUtil.class);
    private static ConfigValueUtil configValueUtil = SpringContext.getBean(ConfigValueUtil.class);
    private static UserProvider userProvider = SpringContext.getBean(UserProvider.class);
    private static FlowDataUtil flowDataUtil= SpringContext.getBean(FlowDataUtil.class);


    /**
     * 去除多级嵌套控件
     *
     * @return
     */
    public static List<FieLdsModel> deleteMoreVmodel(FieLdsModel model) {
        if ("".equals(model.getVModel()) && model.getConfig().getChildren() != null) {
            List<FieLdsModel> childModelList = JsonUtil.getJsonToList(model.getConfig().getChildren(), FieLdsModel.class);
            return childModelList;
        }
        return null;
    }

    public static List<FieLdsModel> deleteMore(List<FieLdsModel> modelList) {
        List<FieLdsModel> newModelList = new ArrayList<>();
        for (FieLdsModel model : modelList) {
            List<FieLdsModel> newList = deleteMoreVmodel(model);
            if (newList == null || JnpfKeyConsts.CHILD_TABLE.equals(model.getConfig().getJnpfKey())) {
                newModelList.add(model);
            } else {
                newModelList.addAll(deleteMore(newList));
            }
        }
        return newModelList;
    }

    /**
     * 返回主键名称
     *
     * @param conn
     * @param mainTable
     * @return
     */
    public static String getpKey(Connection conn, String mainTable) throws SQLException {
        String pKeyName = "f_id";
        //catalog 数据库名
        String catalog = conn.getCatalog();
        @Cleanup ResultSet primaryKeyResultSet = conn.getMetaData().getPrimaryKeys(catalog, null, mainTable);
        while (primaryKeyResultSet.next()) {
            pKeyName = primaryKeyResultSet.getString("COLUMN_NAME");
        }
        primaryKeyResultSet.close();
        return pKeyName;
    }


    /**
     * 获取有表单条数据
     * @param sql
     * @return
     * @throws DataException
     */
    public static List<Map<String, Object>> getTableDataInfo(String sql,DbLinkEntity linkEntity) throws Exception {
       @Cleanup Connection conn=getTableConn();
        if (linkEntity!=null){
            conn = getDataConn(linkEntity);
        }
        List<Map<String, Object>> dataList = JdbcUtil.queryListLowercase(new PrepareSqlDTO(conn, sql));
        for (Map<String, Object> dataMap : dataList) {
            for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
                //判断是否是字符串数组时间戳，如果是则转成Json数组时间戳
                if (entry.getValue() != null && String.valueOf(entry.getValue()).contains(",") && !"[\"".equals(String.valueOf(entry.getValue()).substring(0, 2)) && entry.getValue().toString().contains("]")) {
                    JSONArray list = JsonUtil.getJsonToJsonArray(String.valueOf(entry.getValue()));
                    entry.setValue(list);
                }
            }

        }
        return dataList;
    }

    /**
     * 获取有表的数据库连接
     * @return
     */
    public static Connection getTableConn() {
        String tenId;
        if (!configValueUtil.isMultiTenancy()) {
            tenId = dataSourceUtil.getDbName();
        } else {
            if (!"PostgreSQL".equals(dataSourceUtil.getDbType())) {
                tenId = userProvider.get().getTenantDbConnectionString();
            } else {
                tenId = dataSourceUtil.getDbName();
            }
        }
        try {
            return ConnUtil.getConn(dataSourceUtil,tenId);
        } catch (DataException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 在线开发多数据源连接
     * @return
     */
    public static Connection getDataConn(DbLinkEntity linkEntity) throws SQLException {
        Connection conn = null;
        if (linkEntity != null) {
            try {
                conn = ConnUtil.getConn(linkEntity);
            } catch (DataException e) {
                e.printStackTrace();
            }
        }else {
            conn=getTableConn();
        }
        if (conn == null) {
            throw new SQLException("连接数据库失败");
        }
        return conn;
    }


    /**
     * 导出在线开发的表格
     * @param visualdevEntity
     * @param path
     * @param list
     * @param keys
     * @param userInfo
     * @return
     */
    public static DownloadVO createModelExcel(VisualdevEntity visualdevEntity, String path, List<Map<String, Object>> list, String[] keys, UserInfo userInfo) {
        DownloadVO vo = DownloadVO.builder().build();
        try {
            //去除空数据
            List<Map<String, Object>> dataList = new ArrayList<>();
            for (Map<String, Object> map : list){
                int i = 0;
                for (String key : keys){
                    //子表
                    if (key.startsWith("tableField") || key.startsWith("TABLEFIELD")){
                        String tableField = key.substring(0, key.indexOf("-" ));
                        String field = key.substring(key.indexOf("-") + 1);
                        Object o = map.get(tableField);
                        if (o!=null){
                            List<Map<String, Object>> childList = (List<Map<String, Object>>) o;
                            for (Map<String, Object> childMap : childList){
                                if (childMap.get(field)!=null){
                                    i++;
                                }
                            }
                        }
                    } else {
                        Object o = map.get(key);
                        if (o!= null){
                            i++;
                        }
                    }
                }
                if (i>0){
                    dataList.add(map);
                }
            }

            FormDataModel formDataModel = JsonUtil.getJsonToBean(visualdevEntity.getFormData(), FormDataModel.class);
            List<FieLdsModel> fieLdsModelList = JsonUtil.getJsonToList(formDataModel.getFields(), FieLdsModel.class);
            //递归
            List<FieLdsModel> allFields = new ArrayList<>();
            recursionFields(fieLdsModelList,allFields);

            Map<String, String> mainMap = new HashMap<>();
            allFields.stream().filter(a->!a.getConfig().getJnpfKey().equals(JnpfKeyConsts.CHILD_TABLE)).forEach(m->mainMap.put(m.getVModel(),m.getConfig().getLabel()));
            List<FieLdsModel> childFields = allFields.stream().filter(a -> a.getConfig().getJnpfKey().equals(JnpfKeyConsts.CHILD_TABLE)).collect(Collectors.toList());
            //创建导出属性对象
            List<ExportSelectedModel> child = new ArrayList<>();
            List<ExportSelectedModel> allExportModelList = new ArrayList<>();
            for (String key : keys){
                ExportSelectedModel exportSelectedModel = new ExportSelectedModel();
                if (key.startsWith("tableField") || key.startsWith("TABLEFIELD")){
                    String tableField = key.substring(0, key.indexOf("-"));
                    String field = key.substring(key.indexOf("-")+1);
                    exportSelectedModel.setTableField(tableField);
                    exportSelectedModel.setField(field);
                    child.add(exportSelectedModel);
                } else {
                    exportSelectedModel.setField(key);
                    exportSelectedModel.setLabel(mainMap.get(key));
                    allExportModelList.add(exportSelectedModel);
                }
            }
            Map<String, List<ExportSelectedModel>> childGroups = child.stream()
                .collect(Collectors.groupingBy(ExportSelectedModel::getTableField,LinkedHashMap::new,Collectors.toList()));
            List<String> keyForIndex = new ArrayList<>();
            for (String key:keys){
                keyForIndex.add(key);
            }
            for (Map.Entry<String, List<ExportSelectedModel>> entry : childGroups.entrySet()){
                String key = entry.getKey();
                List<String> collect = keyForIndex.stream().filter(k -> k.startsWith(key)).collect(Collectors.toList());
                String s = keyForIndex.stream().filter(keyF -> keyF.startsWith(key)).findFirst().orElse("");
                int i = keyForIndex.indexOf(s);
                keyForIndex.removeAll(collect);
                List<ExportSelectedModel> value = entry.getValue();
                FieLdsModel fieLdsModel = childFields.stream().filter(c -> c.getVModel().equals(key)).findFirst().orElse(null);
                Map<String, String> childMap = new HashMap<>(16);
                fieLdsModel.getConfig().getChildren().stream().forEach(c->childMap.put(c.getVModel(),c.getConfig().getLabel()));
                value.stream().forEach(v->
                    v.setLabel(childMap.get(v.getField()))
                );
                ExportSelectedModel exportSelectedModel = new ExportSelectedModel();
                exportSelectedModel.setTableField(key);
                exportSelectedModel.setSelectedModelList(value);
                exportSelectedModel.setLabel(fieLdsModel.getConfig().getLabel());
                allExportModelList.add(i, exportSelectedModel);
            }

            List<ExcelExportEntity> entitys = new ArrayList<>();
            for (ExportSelectedModel selectModel : allExportModelList){
                ExcelExportEntity exportEntity;
                if (StringUtil.isNotEmpty(selectModel.getTableField())){
                    exportEntity =new ExcelExportEntity(selectModel.getLabel(),selectModel.getTableField());
                    exportEntity.setList(selectModel.getSelectedModelList().stream().map(m-> new ExcelExportEntity(m.getLabel(),m.getField())).collect(Collectors.toList()));
                } else {
                    exportEntity = new ExcelExportEntity(selectModel.getLabel(),selectModel.getField());
                }
                entitys.add(exportEntity);
            }

            ExportParams exportParams = new ExportParams(null, "表单信息");
            @Cleanup Workbook workbook = new HSSFWorkbook();
            if (entitys.size()>0){
                if (dataList.size()==0){
                    dataList.add(new HashMap<>());
                }
                workbook  = ExcelExportUtil.exportExcel(exportParams, entitys, dataList);
            }
            String fileName = "表单信息" + DateUtil.dateNow("yyyyMMddHHmmss") + ".xls";
            MultipartFile multipartFile = ExcelUtil.workbookToCommonsMultipartFile(workbook, fileName);
            String temporaryFilePath = configValueUtil.getTemporaryFilePath();
            FileInfo fileInfo = FileUploadUtils.uploadFile(multipartFile, temporaryFilePath, fileName);
            vo.setName(fileInfo.getFilename());
            vo.setUrl(UploaderUtil.uploaderFile(fileInfo.getFilename() + "#" + "Temporary") + "&name=" + fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return vo;
    }

    private static void recursionFields(List<FieLdsModel> fieLdsModelList, List<FieLdsModel> allFields){
        for (FieLdsModel fieLdsModel : fieLdsModelList){
            if (JnpfKeyConsts.CHILD_TABLE.equals(fieLdsModel.getConfig().getJnpfKey())){
                allFields.add(fieLdsModel);
            } else {
                if (fieLdsModel.getConfig().getChildren()!=null){
                    recursionFields(fieLdsModel.getConfig().getChildren(),allFields);
                }else {
                    if (StringUtil.isNotEmpty(fieLdsModel.getVModel())){
                        allFields.add(fieLdsModel);
                    }
                }
            }
        }
    }

    /**
     * @param mapList
     * @return List<Map < String, Object>>
     * @Date 21:51 2020/11/11
     * @Description 将map中的所有key转化为小写
     */
    public static List<Map<String, Object>> toLowerKeyList(List<Map<String, Object>> mapList) {
        List<Map<String, Object>> newMapList = new ArrayList<>();
        for (Map<String, Object> map : mapList) {
            Map<String, Object> resultMap = new HashMap(16);
            Set<String> sets = map.keySet();
            for (String key : sets) {
                resultMap.put(key.toLowerCase(), map.get(key));
            }
            newMapList.add(resultMap);
        }
        return newMapList;
    }


    /**
     * @param map
     * @return java.util.Map<java.lang.String, java.lang.String>
     * @Description 将map中的所有key转化为小写
     */
    public static Map<String, Object> toLowerKey(Map<String, Object> map) {
        Map<String, Object> resultMap = new HashMap<>(16);
        Set<String> sets = map.keySet();
        for (String key : sets) {
            resultMap.put(key.toLowerCase(), map.get(key));
        }
        return resultMap;
    }

    public static Boolean getVersion(String table, DbLinkEntity linkEntity, Map dataMap,String id,Integer primaryKey)  {
        String queryVersionSql = "";
        boolean canUpdate = true;
        try {
            @Cleanup Connection conn = getDataConn(linkEntity);
            String pKeyName = flowDataUtil.getKey(conn, table, primaryKey);
             queryVersionSql = "select f_version from " + table + " where " + pKeyName + " = ?";
            PrepareSqlDTO statementDTO = new PrepareSqlDTO(conn,queryVersionSql,id);
            Map<String, Object> map = JdbcUtil.queryOne(statementDTO);
            canUpdate = map.get("f_version").equals(dataMap.get("f_version"));
        } catch (Exception e) {
            System.out.println(queryVersionSql);
            log.error("查询并发锁异常" +queryVersionSql);
            e.printStackTrace();
        }
        return canUpdate;
    }

    /**
     * 审批流提交
     * @param visualdevEntity
     * @param flowTaskId
     * @param formdata
     * @param userInfo
     */
    public static void submitFlowTask(VisualdevEntity visualdevEntity, String flowTaskId, Object formdata, UserInfo userInfo, FlowModel flowModel) throws WorkFlowException {
        //审批流
        if (visualdevEntity.getWebType().equals(VisualWebTypeEnum.FLOW_FROM.getType() )){
            try {
                FlowEngineService flowEngineService = SpringContext.getBean(FlowEngineService.class);
                FlowEngineEntity flowEngineEntity = flowEngineService.getInfo(visualdevEntity.getFlowId());
                FlowTaskService flowTaskService = SpringContext.getBean(FlowTaskService.class);
                FlowTaskNewService flowTaskNewService = SpringContext.getBean(FlowTaskNewService.class);
                FlowTaskEntity flowTaskEntity = flowTaskService.getInfoSubmit(flowTaskId, FlowTaskEntity::getId);
                String id = null;
                if (flowTaskEntity != null) {
                    id = flowTaskEntity.getId();
                }
                String flowTitle = userInfo.getUserName() +"的"+ visualdevEntity.getFullName();
                String billNo ="#Visual"+ DateUtil.getNow();
                flowModel.setId(id);
                flowModel.setFlowId(flowEngineEntity.getId());
                flowModel.setProcessId(flowTaskId);
                flowModel.setFlowTitle(flowTitle);
                flowModel.setBillNo(billNo);
                Map<String, Object> data = JsonUtil.entityToMap(formdata);
                flowModel.setFormData(data);
                flowModel.setUserInfo(userInfo);
                flowTaskNewService.submit(flowModel);
            } catch (WorkFlowException e) {
                throw new WorkFlowException(e.getCode(),e.getMessage());
            }
        }
    }
}
